﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Model;
using Model.Interfaces;
using MvvmCore;
using MvvmCore.ViewModel;

namespace Lab2.ViewModels
{
    public class CauchySystemViewModel : BaseViewModel
    {
        #region Fields

        private ICauchySystem _system;
        private double _step;

        private ObservableCollection<Point> _eulerSolution;
        private ObservableCollection<Point> _modifiedEulerSolution;
        private ObservableCollection<Point> _rungeKuttSolution;
        private ObservableCollection<Point> _adamsSolution;

        private CompositeDataSource _eulerSolutionDataSource;
        private CompositeDataSource _modifiedEulerSolutionDataSource;
        private CompositeDataSource _rungeKuttSolutionDataSource;
        private CompositeDataSource _adamsSolutionDataSource;

        private ICommand _calculateCommand;

        #endregion

        #region Properties

        public ICommand CalculateCommand
        {
            get { return _calculateCommand ?? (_calculateCommand = new RelayCommand(obj => Calculate())); }
        }

        public ICauchySystem System
        {
            get { return _system; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                _system = value;
                OnPropertyChanged("System");
            }
        }

        public double Step
        {
            get { return _step; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("value < 0");
                _step = value;
                OnPropertyChanged("Step");
            }
        }

        public double Start
        {
            get { return _system.A; }
            set
            {
                _system.A = value;
                OnPropertyChanged("Start");
            }
        }

        public double Finish
        {
            get { return _system.B; }
            set
            {
                _system.B = value;
                OnPropertyChanged("Finish");
            }
        }

        public ObservableCollection<Point> EulerSolution
        {
            get { return _eulerSolution; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("Euler solution == null");
                _eulerSolution = value;
                OnPropertyChanged("EulerSolution");
            }
        }

        public ObservableCollection<Point> ModifiedEulerSolution
        {
            get { return _modifiedEulerSolution; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _modifiedEulerSolution = value;
                OnPropertyChanged("ModifiedEulerSolution");
            }
        }

        public ObservableCollection<Point> RungeKuttSolution
        {
            get { return _rungeKuttSolution; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _rungeKuttSolution = value;
                OnPropertyChanged("RungeKuttSolution");
            }
        }

        public ObservableCollection<Point> AdamsSolution
        {
            get { return _adamsSolution; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                _adamsSolution = value;
                OnPropertyChanged("AdamsSolution");
            }
        }

        public CompositeDataSource EulerSolutionDataSource
        {
            get { return _eulerSolutionDataSource; }
            set 
            {
                if (value == null)
                    throw new ArgumentNullException();
                _eulerSolutionDataSource = value;
                OnPropertyChanged("EulerSolutionDataSource");
            }
        }

        public CompositeDataSource ModifiedEulerSolutionDataSource
        {
            get { return _modifiedEulerSolutionDataSource; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                _modifiedEulerSolutionDataSource = value;
                OnPropertyChanged("ModifiedEulerSolutionDataSource");
            }
        }

        public CompositeDataSource RungeKuttSolutionDataSource
        {
            get { return _rungeKuttSolutionDataSource; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                _rungeKuttSolutionDataSource = value;
                OnPropertyChanged("RungeKuttSolutionDataSource");
            }
        }

        public CompositeDataSource AdamsSolutionDataSource
        {
            get { return _adamsSolutionDataSource; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                _adamsSolutionDataSource = value;
                OnPropertyChanged("AdamsSolutionDataSource");
            }
        }

        public double Epsilon
        {
            get { return _system.Epsilon; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("epsilon < 0");
                _system.Epsilon = value;
                OnPropertyChanged("Epsilon");
            }
        }

        #endregion

        #region Constructors

        public CauchySystemViewModel(ICauchySystem system)
        {
            if (system == null)
                throw new ArgumentNullException("system");
            _system = system;
        }

        #endregion

        #region Methods

        private static CompositeDataSource GetCompositeDataSource(IEnumerable<Point> points)
        {
            var enumerable = points as List<Point> ?? points.ToList();
            var xData = new EnumerableDataSource<double>(enumerable.Select(point => point.X));
            xData.SetXMapping(x => x);
            var yData = new EnumerableDataSource<double>(enumerable.Select(point => point.Y));
            yData.SetYMapping(y => y);
            CompositeDataSource dataSource = xData.Join(yData);
            return dataSource;
        }

        public void Calculate()
        {
            // euler calculation
            EulerSolution = new ObservableCollection<Point>(System.EulerSolution(Step));
            EulerSolutionDataSource = GetCompositeDataSource(EulerSolution);

            // modified euler solution
            ModifiedEulerSolution = new ObservableCollection<Point>(System.ModifiedEulerSolution(Step));
            ModifiedEulerSolutionDataSource = GetCompositeDataSource(ModifiedEulerSolution);

            // runge kutt solution
            RungeKuttSolution = new ObservableCollection<Point>(System.RungeKuttSolution(Step));
            RungeKuttSolutionDataSource = GetCompositeDataSource(RungeKuttSolution);

            // adams
            AdamsSolution = new ObservableCollection<Point>(System.Calculate(Step));
            AdamsSolutionDataSource = GetCompositeDataSource(AdamsSolution);
        }

        #endregion
    }
}
